package utils

import (
	"context"
	"errors"
	"fmt"
	"math"
	"net"
	"regexp"
	"strings"
	"syscall"

	"github.com/google/uuid"
	"github.com/rs/xid"
	"gorm.io/gorm"
)

// SetValForContext 设置上下文的键值
//
// 参数:
//
//   - ctx:上下文
//   - key:键
//   - value:值
func SetValForContext(ctx context.Context, key string, value any) context.Context {
	return context.WithValue(ctx, key, value)
}

// GetValForContext 从上下文获取值
//
// 参数:
//
//   - ctx:上下文
//   - key:键
//
// 返回:
//   - string:获取的值
func GetValForContext(ctx context.Context, key string) string {
	val, ok := ctx.Value(key).(string)
	if !ok {
		return ""
	}
	return val
}

// PageParam 分页参数结构体
type PageParam struct {
	Page      int64 // 当前页码
	Size      int64 // 每页条数
	Offset    int64 // 偏移量
	Total     int64 // 总条数
	TotalPage int64 // 总页数
}

// InitPageParam 始化分页参数
//
// 参数:
//
//   - page: 当前页码
//   - size: 每页条数
//
// 返回:
//   - *PageParam: 分页结构
func InitPageParam(page, size int64) *PageParam {
	if page <= 0 {
		page = 1
	}
	if size <= 0 {
		size = 10
	}
	return &PageParam{
		Page:   page,
		Size:   size,
		Offset: (page - 1) * size,
	}
}

// SetOrder 设置排序 (安全处理)
//
// 参数:
//
//   - db: *gorm.DB
//   - column: 排序的列
//   - order: 升序或者降序
//
// 返回:
//   - *PageParam: 分页结构
func SetOrder(db *gorm.DB, column, order string) *gorm.DB {
	if column == "" || order == "" {
		return db.Order("id ASC")
	}

	// 防止 SQL 注入 - 校验列名格式
	validColumn := regexp.MustCompile(`^[a-zA-Z0-9_]+$`).MatchString
	if !validColumn(column) {
		return db.Order("id ASC")
	}

	// 标准化排序方向
	order = strings.ToUpper(order)
	if order != "ASC" && order != "DESC" {
		order = "ASC"
	}

	return db.Order(fmt.Sprintf("%s %s", CamelToSnakeCase(column), order))
}

// CalcTotalPage 计算总页数
func CalcTotalPage(total, size int64) int64 {
	return int64(int(math.Ceil(float64(total) / float64(size))))
}

// NormalUUID 返回常规UUID
func NormalUUID() string {
	return uuid.NewString()
}

// ShortUUID 返回短UUID
func ShortUUID() string {
	return xid.New().String()
}

// IsAddrInUseErr 端口号是否被使用Err
//
// 参数:
//
//   - err: 错误
//
// 返回:
//   - bool: 是否是该类型错误
func IsAddrInUseErr(err error) bool {
	var opErr *net.OpError
	if errors.As(err, &opErr) {
		var sysErr syscall.Errno
		if errors.As(opErr.Err, &sysErr) {
			return sysErr == syscall.EADDRINUSE
		}
	}
	return false
}
